﻿using System;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
namespace AE.Remoting
{
    [Serializable()]
    class Serializable {
        Type _Type;
        Dictionary<string, object> _Values;

        public static Serializable Serialize<T>(T obj) {
            return Serialize(obj, obj == null || typeof(T) != typeof(object) ? typeof(T) : obj.GetType());
        }

        public static Serializable Serialize(object obj, Type type) {
            return Serialize(obj, type, new Dictionary<object, Serializable>());
        }

        private static Serializable Serialize(object obj, Type type, Dictionary<object, Serializable> history) {
            var fields = type.GetCachedFields();
            var serialized = new Serializable();
            history.Add(obj, serialized);

            serialized._Type = type;
            Dictionary<string, object> values = new Dictionary<string, object>();

            if (obj != null) {
                foreach (var field in fields) {
                    object value = field.FastGet(obj);
                    if (!field.FieldType.IsSerializable) {
                        if (history.ContainsKey(value))
                            value = history[value];
                        else
                            value = Serialize(value, field.FieldType, history);
                    }
                    values.Add(field.DeclaringType.FullName + "." + field.Name, value);
                }

                serialized._Values = values;
            }

            return serialized;
        }

        public object Deserialize() {
            return Deserialize(new Dictionary<Serializable, object>());
        }

        public object Deserialize(Dictionary<Serializable, object> history) {
            if (_Values == null) return null;
            object obj;
            if (_Type.Name.Contains("AnonymousType")) {
                obj = _Type.FastCreate(_Values.Values.ToArray());
                history.Add(this, obj);
            } else {
                obj = _Type.FastCreate();
                history.Add(this, obj);

                var fields = _Type.GetCachedFields();
                foreach (var field in fields) {
                    object value = _Values[field.DeclaringType.FullName + "." + field.Name];
                    if (value is Serializable) {
                        if (history.ContainsKey((Serializable)value))
                            value = history[(Serializable)value];
                        else value = ((Serializable)value).Deserialize();
                    }
                    field.FastSet(obj, value);
                }
            }
            return obj;
        }
    }
}
